home *** CD-ROM | disk | FTP | other *** search
/ Delphi 2.0 - Programmer's Utilities Power Pack / Delphi 2.0 Programmer's Utilities Power Pack.iso / s_to_z / tpack / okcore.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-09-15  |  12.7 KB  |  395 lines

  1. unit OkCore; {Home of TOk, the OK Component.}
  2.  
  3. {base components to start a process and signal/negotiate go/stop.}
  4.  
  5. {Sounds simple, but boy, this was my first component and kept me scratching my head
  6. for an embaressingly long time. YOU need this functionality now and here it is.}
  7.  
  8. interface
  9.  
  10. uses
  11.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  12.   Forms, Dialogs, StdCtrls
  13. , Retry
  14. , PasUtils
  15. , UserInfo;
  16.  
  17. type
  18.  
  19. {------------------------------------------------------------------------------}
  20. {TOk defines the essential stopability that other components rely on.
  21. It requests permission to change states and performs tries on on/off
  22. it also can disable 'other' windows when running to make is seem modal. <explore this.useful}
  23.  
  24.   TOk = class;
  25.  
  26.   TOkState = (stsActive,stsCritical,stsReady,stsCanceled,stsDisabled);  {must improve!}
  27.  
  28.   TOkAware = class(TDialogShell)
  29.   private
  30.     fEnabled:        Boolean;  {allow changes only if enabled}
  31.     fActive:         Boolean;   {read only, true until done}
  32.     fProcessMessages: Boolean;
  33.   protected
  34.     procedure SetActive(Flag:Boolean);  virtual;
  35.     function GetActive:Boolean;
  36.   public
  37.     constructor Create(AOwner:TComponent); override;
  38.     procedure Execute; override;
  39.   published
  40.     property  Active: Boolean   read GetActive   write SetActive;
  41.     property  Enabled: Boolean  read fEnabled  write fEnabled default true;
  42.     property  ProcessMessages: Boolean read fProcessMessages write fProcessMessages default true;
  43.   end;
  44.  
  45. {------------------------------------------------------------------------------}
  46.  
  47.   EOkAlreadyActive = class(Exception);
  48.   EOkDoAlreadyActive = class(EOkAlreadyActive);
  49.  
  50. {------------------------------------------------------------------------------}
  51.  
  52.   TOkOnOkStart  = procedure(Sender: TOk;Var CanStart:Boolean) of object;
  53.   TOkOnOkStop   = procedure(Sender: TOk;Var CanStop:Boolean) of object;
  54.   TOkOnOkChange = procedure(Sender: TOk;NewState:TOkState;Var CanChange:Boolean) of object;
  55.  
  56.   TOk = class(TOkAware)
  57.     {TOk remains completely invisible but let you hookup begin/end procs that
  58.     make it simple for you to hook up changes to captions or do whatever
  59.     you need to keep the user happy while running your Ok action. To use,
  60.     set Active:=True when beginning your loop, then check 'Active' or 'Stop'
  61.     while looping to Process Messages and exit properly. Your Cancel button can
  62.     signal a normal or cancel outcome by setting Active:=False or Canceled:=True.
  63.     Deactivating in either ways can be denied by the OnOkStop procedure.}
  64.  
  65.     {simple, right?
  66.      you could call this component from a button like this:
  67.       Ok1.Active:=not Ok1.Active;
  68.       if Ok1.Active then
  69.         Button1.Caption:='Running'
  70.       else
  71.         Button1.Caption:='Stopped';
  72.     }
  73.   private
  74.     { Private declarations }
  75.     fCritical:       Boolean; {disable OkBox button, on hold}
  76.     fCanceled:       Boolean; {Ok canceled on last try}
  77.     fFrozen:         Boolean;   {other forms disabled while true}
  78.     fFreeze:         Boolean;   {disable other forms while true}
  79.     fOnOkStart:      TOkOnOkStart;{OnOkStart proc to ok-ok. eg. oks ative=true}
  80.     fOnOkChange:     TOkOnOkChange;{ok-ok. eg. oks ative=true}
  81.     fOnOkStop:       TOkOnOkStop;  {ok-ok. eg. oks ative=true}
  82.   protected
  83.     { Protected declarations }
  84.     procedure   SetActive(Flag:Boolean);             override;
  85.     procedure   SetEnabled(Flag:Boolean);            virtual;
  86.     procedure   SetStop(Flag:Boolean);               virtual;
  87.     procedure   SetCritical(Flag:Boolean);           virtual;
  88.     procedure   SetCanceled(Flag:Boolean);           virtual;
  89.     procedure   SetState(State:TOkState);            virtual;
  90.     procedure   SetFrozen(Flag:Boolean;ButNot:HWND); virtual;
  91.     function    GetStop:Boolean;
  92.     function    GetState:TOkState;
  93.     function    GetStringState:String;
  94.     procedure   DoOkStart(Var CanStart:Boolean);                     virtual;
  95.     procedure   DoOkChange(NewState:TOkState;Var CanChange:Boolean); virtual;
  96.     procedure   DoOkStop(Var CanStop:Boolean);                       virtual;
  97.     function    FreezeFormHandle:HWND;               virtual;
  98.   public
  99.     { Public declarations }
  100.     constructor Create(AOwner:TComponent);           override;
  101.     procedure   Run(Sender:TObject;Var Success: Boolean); virtual;
  102.     procedure   OkOn;                                virtual;
  103.     procedure   OkOff;                               virtual;
  104.     procedure   Reset;                               virtual;
  105.     function    BenchmarkLoopsPerSecond:LongInt;
  106.     function    StringState(State:TOkState):String;
  107.   published
  108.     property Enabled: Boolean            read fEnabled  write SetEnabled default true;
  109. {    property Active: Boolean             read fActive   write SetActive;}
  110.     {note if you disable, only enabled and state will change. you can not
  111.     disable the control in a critical section.}
  112.     property Ok: Boolean                 read fActive   write SetActive; {ALIAS}
  113.     property Stop: Boolean               read GetStop   write SetStop default true;
  114.     property Critical: Boolean           read fCritical write SetCritical;
  115.     property Canceled: Boolean           read fCanceled write SetCanceled;
  116.     property State: TOkState             read GetState  write SetState default stsReady;
  117.     property StateString: String         read GetStringState;
  118.     property FreezeForms: Boolean        read fFreeze   write fFreeze;
  119.     property OnOkStart: TOkOnOkStart     read fOnOkStart write fOnOkStart;
  120.     property OnOkChange: TOkOnOkChange   read fOnOkChange write fOnOkChange;
  121.     property OnOkStop: TOkOnOkStop       read fOnOkStop write fOnOkStop;
  122.   end;
  123.  
  124.  
  125. implementation
  126.  
  127. {------------------------------------------------------------------------------}
  128.  
  129. constructor TOkAware.Create(AOwner:TComponent);
  130. begin
  131.   inherited create(AOwner);
  132.   fEnabled:=true;
  133.   fProcessMessages:=true;
  134. end;
  135.  
  136. procedure TOkAware.SetActive(Flag:Boolean);
  137. begin
  138.   if fActive<>Flag then
  139.     fActive:=Flag;
  140. end;
  141.  
  142. function TOkAware.GetActive:Boolean;
  143. begin
  144.   if fProcessMessages then
  145.     Application.ProcessMessages;
  146.   Result:=fActive;
  147. end;
  148.  
  149. procedure TOkAware.Execute;
  150. begin
  151.   Active:=True;
  152. end;
  153.  
  154. {------------------------------------------------------------------------------}
  155. {Let's begin.. here goes the 'root' component, e.g.  the Ok capability.}
  156.  
  157. constructor TOk.Create(AOwner:TComponent);
  158. begin
  159.   inherited create(AOwner);
  160.   fEnabled:=True;
  161. end;
  162.  
  163. procedure TOk.Reset;
  164. {Unconditially resets the component and puts it in ready mode. use at own risk.
  165. {this methd allows you to stop a OkTry regardless of the callback method's
  166. opinion. we simply take it out, shut off and put it back. it never knows.}
  167. var
  168.   e:TOkOnOkStop;
  169. begin
  170.   e:=fOnOkStop;
  171.   fOnOkStop:=nil;
  172.   State:=stsReady;
  173.   {note: this is the only time we actually resort to changing the component's
  174.   state. usually we just manipulate the flags directly but here we want to take
  175.   advantage of the override logic in SetState.}
  176.   {you might think we should make the status canceled if we shut down a loop.
  177.   that wouldn't be right either because we really 'excepted' out of the OkTry.}
  178.   fOnOkStop:=e;
  179. end;
  180.  
  181. procedure TOk.Run(Sender:TObject;Var Success: Boolean);
  182. begin
  183.   SetActive(True);
  184.   SetActive(False);
  185. end;
  186.  
  187. procedure TOk.OkOn;
  188. begin
  189.   Active:=True;
  190. end;
  191.  
  192. procedure TOk.OkOff;
  193. begin
  194.   Active:=False;
  195. end;
  196.  
  197. function TOk.BenchmarkLoopsPersecond:Longint;
  198. begin
  199.   result:=-1;
  200.   {instantiate timer w/proc to signal end (could use another ok)
  201.   then count how often we can turn ok on/off inside that time.}
  202. end;
  203.  
  204. procedure TOk.SetEnabled(Flag:Boolean);
  205. begin
  206.   if Flag<>fEnabled then begin
  207.     if (not Flag) and fActive and fCritical then {can not stop in a critical section!}
  208.       Exit;
  209.     if fActive and (Flag=false) then
  210.       Active:=False; {turn off. OnOkStop may deny.}
  211.    {implement okchange!}
  212.     fEnabled:=fActive or Flag;
  213.     end;
  214. end;
  215.  
  216. procedure TOk.SetCanceled(Flag:Boolean);
  217. begin
  218.   if fEnabled and Flag<>fCanceled then begin
  219.     if flag then       {do not activate when resetting flag}
  220.       SetStop(Flag);
  221.     if Flag<>fCanceled then begin
  222.       DoOkChange(stsCanceled,Flag);
  223.       fCanceled:=Flag;
  224.       end;
  225.     end;
  226. end;
  227.  
  228. procedure TOk.SetStop(Flag:Boolean);
  229. begin
  230.   Active:=not Flag;
  231. end;
  232.  
  233. function TOk.GetStop:Boolean;
  234. begin
  235.   Result:=not Active;
  236. end;
  237.  
  238. procedure TOk.SetActive(Flag:Boolean);
  239. var
  240.   Close: Boolean;
  241. begin
  242.   if fEnabled and Flag<>fActive then begin
  243.     if Flag then begin
  244.       if fActive then
  245.         raise EOkAlreadyActive.Create('TOk: Already Active');
  246.       fCanceled:=False;
  247.       DoOkChange(stsActive,Flag);
  248.       if flag then
  249.         DoOkStart(Flag);
  250.       fActive:=false;
  251.       if not flag then
  252.         exit;
  253.       end
  254.     else begin
  255.       if fActive and fCritical then {can not stop in a critical section!}
  256.         Exit;
  257.       Close:= true;
  258.       DoOkChange(stsReady,close);
  259.       if Close then
  260.         DoOkStop(close);
  261.       fActive:=true;
  262.       if not Close then
  263.         exit;
  264.       end;
  265.     if flag<>fActive then begin
  266.       fActive:=Flag;
  267.       SetFrozen(fActive and fFreeze,FreezeFormHandle);
  268.       end;
  269.     end;
  270. end;
  271.  
  272. procedure TOk.SetCritical(Flag:Boolean);
  273. {OkTry can not be stopped when in a critical section}
  274. {it can start in 'critical' mode where it can not be stopped-
  275. however the component can not be enabled without resetting critical to neutral,
  276. note that 'enabling' is not 'activating'. you can go from ready mode to critical,
  277. just going from disabled to critical is not possible. makes sense?}
  278. begin
  279.   if fEnabled and Flag<>fCritical then
  280.     fCritical:={fActive and} Flag;
  281. end;
  282.  
  283. procedure TOk.SetState(State:TOkState);
  284. {by setting the state, you get a shortcut way to change the properties
  285. you want to change. REMEMBER!: CRITICAL=TRUE forces the box to stay on,
  286. ENABLE=FALSE forces it to stay off. no matter how often you try, these
  287. properties will block you from changing others. In critical sections the
  288. OnOkStop procedure is never called.}
  289. begin
  290.   case State of
  291.     stsActive:   if fCritical and fActive then
  292.                    Critical:= False {transit back from critical to active}
  293.                  else
  294.                    Active:=   True;
  295.     stsCritical: if fActive then
  296.                    Critical:= True;
  297.     stsReady:    begin
  298.                  if fEnabled=false then
  299.                    fEnabled:=True;
  300.                  if fCritical then
  301.                    fCritical:=False;
  302.                  Canceled:=False;
  303.                  end;
  304.     stsCanceled: Canceled:= True;
  305.     stsDisabled: Enabled:=  False;
  306.     end;
  307. end;
  308.  
  309. function TOk.GetState:TOkState;
  310. {you definitely must play with this component in the object inspector before
  311. using it. the CRITICAL/ENABLED flags must be understood to be useful. the 'State'
  312. property should make the logic clearer.}
  313. begin
  314.   if not fEnabled then
  315.     Result:=stsDisabled
  316.   else
  317.     if fCanceled then
  318.       Result:=stsCanceled
  319.     else
  320.       if not fActive then
  321.         Result:=stsReady
  322.       else
  323.         if fCritical then
  324.           Result:=stsCritical
  325.         else
  326.           Result:=stsActive;
  327.   if fProcessMessages then
  328.     Application.ProcessMessages;
  329. end;
  330.  
  331. function TOk.GetStringState:String;
  332. begin
  333.   result:=StringState(State);
  334. end;
  335.  
  336. function TOk.StringState(State:TOkState):String;
  337. begin
  338.   case State of
  339.   stsActive:      Result:='Active';
  340.   stsCritical:    Result:='Critical';
  341.   stsReady:       Result:='Ready';
  342.   stsCanceled:    Result:='Canceled';
  343.   stsDisabled:    Result:='Disabled';
  344.   end;
  345. end;
  346.  
  347. procedure TOk.SetFrozen(Flag:Boolean;ButNot:HWND);
  348. var
  349.   i:longint;
  350. begin
  351.   if Flag<>fFrozen then begin
  352.     fFrozen:=Flag;
  353.     for i:=0 to Screen.FormCount-1 do
  354.       if ButNot <> Screen.Forms[i].Handle then
  355.         with Screen.Forms[i] do
  356.           Enabled := not Enabled;
  357.     end;
  358. end;
  359.  
  360. function TOk.FreezeFormHandle:HWND;
  361. {the purpose of this function is to be replaced by a descendant in case the
  362. usual choice of forms to be unfrozen is not right, and frankly, to allow us
  363. to focus either on the derived OkBox or on the currently active form}
  364. begin
  365.   result:=Screen.ActiveForm.Handle;
  366. end;
  367.  
  368. {}
  369.  
  370. procedure TOk.DoOkStart(Var CanStart:Boolean);
  371. begin
  372.   if assigned(fOnOkStart) then
  373.     fOnOkStart(Self,CanStart);
  374.   if CanStart then begin
  375.     fActive:=CanStart;
  376.     fActive:=CanStart;
  377.     end;
  378. end;
  379.  
  380. procedure TOk.DoOkChange(NewState:TOkState;Var CanChange:Boolean);
  381. begin
  382.   if assigned(fOnOkChange) then fOnOkChange(Self,NewState,CanChange);
  383. end;
  384.  
  385. procedure TOk.DoOkStop(Var CanStop:Boolean);
  386. begin
  387.   if assigned(fOnOkStop) then
  388.     fOnOkStop(Self,CanStop);
  389. end;
  390.  
  391. {------------------------------------------------------------------------------}
  392.  
  393. end.
  394.  
  395.